//Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Microsoft.Office.OneNote
{
	/// <summary>
	/// Locks a node and all of it's children for updating, and commits those
	/// changes upon disposing.
	/// </summary>
	public class HierarchyTransaction : IDisposable
	{
		public HierarchyTransaction(HierarchyNode scope)
		{
			this.transactionScope = scope;
			this.Synchronize(scope);
			this.writeLock = new HierarchySynchronizationLocker(scope, HierarchySynchronizationLocker.Mode.ForWrite);
		}

		~HierarchyTransaction()
		{
			Dispose(false);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!this.disposed)
			{
				Debug.Assert(disposing, "Transaction not property wrapped in a using block.");

				Commit();

				if (this.writeLock != null)
				{
					this.writeLock.Dispose();
					this.writeLock = null;
				}

				this.transactionScope = null;

				this.disposed = true;
			}
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		public void Commit()
		{
			if (this.disposed)
				throw new ObjectDisposedException(this.GetType().Name);

			if (this.transactionScope != null && this.transactionScope.CommitPending)
			{
				try
				{
					this.transactionScope.Commit();
				}
				catch (Exception)
				{
					// Throw away the changes:
					this.transactionScope.CommitPending = false;
					this.Synchronize(this.transactionScope);

					throw;
				}

				this.Synchronize(this.transactionScope);
			}
		}

		public void Synchronize(HierarchyNode target)
		{
			target.EnsureChildrenSynchronized(true /* deep */, true /* force */);
		}

		private bool disposed = false;

		private HierarchyNode transactionScope = null;
		private HierarchySynchronizationLocker writeLock = null;
	}
}
